; ==========================================
; Build: nasm load.asm -o load.bin
; ==========================================

%include	"load_pm.asm"	; 常量, 宏, 以及一些说明

; FreeDOS 加载，需要org 0x100
org	0x100
jmp	LABEL_BEGIN

; GDT
[SECTION .gdt]
;								  段基址,		    段界限,		    属性.
LABEL_GDT:			Descriptor	       0,                0,			    0	; 空描述符 
LABEL_DESC_CODE32:	Descriptor	       0, 		0xffffffff, DA_CR + DA_32	; 非一致代码段, 32
LABEL_DESC_VIDEO:	Descriptor	 0xB8000,           0xffff, 	   DA_DRW	; 显存首地址
; GDT 结束

GdtLen		equ	$ - LABEL_GDT	; GDT 长度
GdtPtr		dw	GdtLen - 1		; GDT 界限
			dd	0				; GDT 基地址

; GDT 选择子
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
; END of [SECTION .gdt]

; IDT
[SECTION .idt]
ALIGN	32
[BITS	32]
LABEL_IDT:
; 门                目标选择子,              偏移,     DCount,		 属性.
%rep 32
		Gate	SelectorCode32, 				0,   		0, DA_386IGate
%endrep
.0x20:	Gate	SelectorCode32,		 ClockHandler,			0, DA_386IGate
.021h:	Gate	SelectorCode32,   KeyBoardHandler,			0, DA_386IGate
%rep 221
		Gate	SelectorCode32, 				0,   		0, DA_386IGate
%endrep
; IDT END

IdtLen		equ	$ - LABEL_IDT	; IDT 长度
IdtPtr		dw	IdtLen - 1		; IDT 界限
			dd	0				; IDT 基地址
; END of [SECTION .idt]

[SECTION .data]; 数据段
ALIGN	32
[BITS	32]
; 实模式下使用这些符号
_dwMCRNumber:			dd	0	; Memory Check Result
_dwMemSize:				dd	0
; Address Range Descriptor Structure
_ARDStruct:						
	_dwBaseAddrLow:		dd	0
	_dwBaseAddrHigh:	dd	0
	_dwLengthLow:		dd	0
	_dwLengthHigh:		dd	0
	_dwType:			dd	0
; END of _ARDStruct
_MemChkBuf:	times	256	db	0

; END of [SECTION .data]

[SECTION .s16]
[BITS	16]
	
%include "load_getmsize.asm"

LABEL_BEGIN:

	; 清理屏幕显示
	mov al, 0x3
	mov ah, 0
	int 0x10
	
	; 置光标位置
	mov bh, 0
	mov dh, 24
	mov dl, 80
	mov ah, 2
	int 0x10
	
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0x100

	; 得到内存数
	mov	ebx, 0
	mov	di, _MemChkBuf
.loop:
	mov	eax, 0xE820
	mov	ecx, 20
	mov	edx, 0x534D4150
	int	0x15
	jc	.LABEL_MEM_CHK_FAIL
	add	di, 20
	inc	dword [_dwMCRNumber]
	cmp	ebx, 0
	jne	.loop
	jmp	.LABEL_MEM_CHK_OK
.LABEL_MEM_CHK_FAIL:
	mov	dword [_dwMCRNumber], 0
.LABEL_MEM_CHK_OK:
	call	GetMemSize
	
.INIT_32CODE:
	; 初始化32位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_GDT			; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址
	
	; 为加载 IDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_IDT			; eax <- idt 基地址
	mov	dword [IdtPtr + 2], eax	; [IdtPtr + 2] <- idt 基地址 

	; 加载 GDTR
	lgdt	[GdtPtr]
	
	; 加载 IDTR
	lidt	[IdtPtr]

	; 关中断
	cli

	; 打开地址线A20
	in	al, 0x92
	or	al, 00000010b
	out	0x92, al

	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; 真正进入保护模式
	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入
[BITS	32]

LABEL_SEG_CODE32:	

.INSTALL_KERNEL:
	mov esi, KERNEL
	mov edi, 0x100000
	mov ecx, 0x80000 / 4 + 1
	call memcpy

.TO_KERNEL:
	; 初始化PIC
	call	Init8259A
	; 初始化PIT，设置定时器频率
	call	InitPIT
	; 开放中断处理关卡
	call	open_handler

	; 参数三 段首对应EIP
	call get_eip0
	push	eax
	; 参数二 实际内存大小
	mov	eax, [_dwMemSize]
	push	eax
	; 参数一 视频段选择子
	mov	eax, SelectorVideo
	push	eax

	jmp	0x100000

%include "load_mem.asm"
%include "load_eip.asm"
%include "load_irq.asm"

KERNEL:
	
